using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Web;
using Conusic.Properties;

namespace Conusic
{
    public static class VkontakteAudio
    {
        public static List<AudioRecord> SearchAudio(string stringToSearch, bool lyrics = false)
        {
            return SearchAudio(null, stringToSearch, lyrics);
        }

        private static List<AudioRecord> SearchAudio(IAudioSearchCommand command, string stringToSearch,
                                                     bool lyrics = false)
        {
            //Wait for Vkontakte access tube
            Config.Instance.VkontakteAccessTube.Wait();

            List<AudioRecord> listAudio = null;

            try
            {
                var urlEndodedSearch = HttpUtility.UrlEncode(stringToSearch);

                var request =
                    (HttpWebRequest)
                    WebRequest.Create(String.Format("{0}/gsearch.php?section=audio&q={1}&c[lyrics]={2}",
                                                    Settings.Default.VkontakteAddress, urlEndodedSearch,
                                                    lyrics ? 1 : 0));

                //parametrize authorization(cookie)
                Config.Instance.SetVkontakteCookieForWebRequest(request);
                //parametrize proxy configuration
                Config.Instance.SetProxyConfiguration(request);

                var stream = new WebResponseCp1251Reader(request.GetResponse());
                string page = stream.ReadToEndAndCloseAll();

                listAudio = ParseAudioPage(page);
            }
            catch (Exception e)
            {
                //todo fix bug expression
                Console.WriteLine(e.ToString());
            }
            if (command != null)
            {
                command.Execute(listAudio);
            }
            return listAudio;
        }


        public static string GetLyrics(string lid)
        {
            //Wait for Vkontakte access tube
            Config.Instance.VkontakteAccessTube.Wait();

            var request =
                (HttpWebRequest)
                WebRequest.Create(String.Format("{0}audio.php?act=getLyrics&lid={1}",
                                                Settings.Default.VkontakteAddress, lid));

            //parametrize proxy and authorization(cookie)
            Config.Instance.SetProxyConfiguration(request);
            Config.Instance.SetVkontakteCookieForWebRequest(request);


            var response = request.GetResponse();

            var datastream = response.GetResponseStream();
            var reader = new StreamReader(datastream, Encoding.GetEncoding(1251));

            string page = reader.ReadToEnd();

            reader.Close();
            datastream.Close();
            response.Close();

            page = page.Replace("<br>", "\n");

            return page;
        }

        /// <summary>
        /// request bitrate for given record in currend thread
        /// </summary>
        public static int GetAverageBitrate(AudioRecord record)
        {
            Config.Instance.BitratePool.WaitOne();

            int averageBitrate = 0;
            try
            {
                var request = WebRequest.Create(record.Url);
                request.Method = "HEAD";
                request.Timeout = 5000;
                var We = request.GetResponse() as HttpWebResponse;
                int size = int.Parse(We.Headers.Get("Content-Length"));

                averageBitrate = Convert.ToInt32(size*8/1024.0/record.Duration);
            }
            catch
            {
            }
            Config.Instance.BitratePool.Release();
            return averageBitrate;
        }

        private const string audioRegex =
            @"<div class=""audioRow"" id=""audio[0-9_]*\"".{0,256}" +
            @"operate\(([0-9]*),([0-9]*),([0-9]*),'([a-z0-9]*)',([0-9]*)\).{0,256}?" +
            @"<b id=""performer[0-9]*"">([^<]*)<.{0,64}id=""title[0-9]*"">" +
            @"(.{0,25}showLyrics\([0-9]*,([0-9]*)\);'>)?([^<]*)";

        private const int
            SERVER_ID_IN_REGEXPR = 2,
            USER_ID_IN_REGEXPR = 3,
            LAST_URL_PART_IN_REGEXPR = 4,
            DURATION_IN_REGEXPR = 5,
            ARTISH_IN_REGEXPR = 6,
            LYRIC_ID_IN_REGEXPR = 8,
            COMPOSITION_NAME_IN_REGEXPR = 9;

        private static List<AudioRecord> ParseAudioPage(string page)
        {
            var listAudio = new List<AudioRecord>();
            var regexpr = new Regex(audioRegex, RegexOptions.Singleline);
            Match match = regexpr.Match(page);

            while (match.Success)
            {
                var record = new AudioRecord();
                //int id = int.Parse (match.Groups[1].ToString ());
                string url = "http://cs" + match.Groups[SERVER_ID_IN_REGEXPR] +
                             ".vkontakte.ru/u" + match.Groups[USER_ID_IN_REGEXPR] +
                             "/audio/" + match.Groups[LAST_URL_PART_IN_REGEXPR] + ".mp3";
                record.CompositionName = match.Groups[COMPOSITION_NAME_IN_REGEXPR].ToString();
                record.Artist = match.Groups[ARTISH_IN_REGEXPR].ToString();
                record.Url = url;
                record.Duration = int.Parse(match.Groups[DURATION_IN_REGEXPR].ToString());
                if (match.Groups[LYRIC_ID_IN_REGEXPR].ToString() != string.Empty)
                {
                    record.VkontakteLyricId = int.Parse(match.Groups[LYRIC_ID_IN_REGEXPR].ToString());
                }

                listAudio.Add(record);

                match = match.NextMatch();
            }
            return listAudio;
        }

        public static void SearchAudioAsync(IAudioSearchCommand command, string stringToSearch, bool lyrics = false)
        {
            ThreadStart threadStart = delegate { SearchAudio(command, stringToSearch, lyrics); };
            var thread = new Thread(threadStart);
            thread.Start();
        }

        public static void SearchOneAudioAsync(IAudioOneSearchCommand command, string searchArtistSpaceName)
        {
            ThreadStart threadStart = delegate { SearchOneAudio(command, searchArtistSpaceName); };
            var thread = new Thread(threadStart);
            thread.Start();
        }

        public static AudioRecord SearchOneAudio(string searchArtistName, int preferedBitrate = 270,
                                                 int audioRecordCountForBitrateFind = 4)
        {
            return SearchOneAudio(null, searchArtistName, preferedBitrate, audioRecordCountForBitrateFind);
        }

        private static AudioRecord SearchOneAudio(IAudioOneSearchCommand command, string searchArtistName,
                                                  int preferedBitrate = 270, int audioRecordCountForBitrateFind = 4)
        {
            var list = SearchAudio(searchArtistName);
            return SongChooser.OptimizeFindRecordInList(list, searchArtistName, preferedBitrate,
                                                        audioRecordCountForBitrateFind);
        }

        public static List<String> GetHints(string enteredChars)
        {
            var result = new List<string>();

            try
            {
                string page = SimpleHttp.RequestVkontaktePage(
                    "hints.php?act=a_gsearch_hints",
                    String.Format("al=1&q={0}&section=audio", HttpUtility.UrlEncode(enteredChars)));

                ParseHints(ref page, ref result);
            }
            catch (WebException)
            {
            }

            return result;
        }

        private static void ParseHints(ref string page, ref List<string> list)
        {
            var regex = new Regex(@"\[[^,]*,[^,]*,[^,]*,""([^""]*)""");
            foreach (Match match in regex.Matches(page))
            {
                list.Add(match.Groups[1].ToString());
            }
        }

        public static string AudioListToJson(IEnumerable<AudioRecord> list)
        {
            if (list == null) return "[]";
            var result = new StringBuilder();
            result.AppendLine("[");
            foreach (var audioRecord in list)
            {
                result.AppendLine(String.Format("{0},", audioRecord.Json()));
            }
            result.Remove(result.Length - 1 - Environment.NewLine.Length, 1);
            result.AppendLine("]");
            return result.ToString();
        }

    }
}